/*
 * SerialReader.cpp
 *
 *
 *  Created on: Oct 8, 2011
 *  Author: Mario H. Voorsluys
 *	email: forcaeluz@gmail.com
 *
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Library General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, visit:
 * http://www.gnu.org/copyleft/gpl.html
 */

#include "cmd_err_src.h"

#include "SerialReader.h"

SerialReader::SerialReader(char* device) {
	device_name = device;
}

SerialReader::~SerialReader() {
	if(fd != -1){
		tcsetattr(fd, TCSAFLUSH, &old_config);
		close(fd);

	}
}

bool SerialReader::open_connection(char* device, int timeout){
	if(device != 0){
		device_name = device;
	}
	fd = open(device, O_RDWR | O_NOCTTY);

	if(fd == -1){
		std::cerr << "Unable to open " << device_name << std::endl;
		return false;
	}

	if(!isatty(fd)){
		std::cerr << "Device " << device_name << " is not a serial port" << std::endl;
		close(fd);
		return false;
	}
	tcgetattr(fd, &config);
	cfmakeraw(&config);
	config.c_cc[VMIN]  = 10;
	config.c_cc[VTIME] = 0;
	config.c_cflag |= CS8 | CLOCAL | CREAD;
	if(cfsetispeed(&config, B38400) < 0 || cfsetospeed(&config, B38400) < 0) {
		std::cerr << "Device " << device_name << " could not work at this speed" << std::endl;
		close(fd);
		return false;
	}

	if(tcsetattr(fd, TCSAFLUSH, &config)){
		std::cerr << "Device " << device_name << " could not work with this config." << std::endl;
		close(fd);
		return false;
	}

	int received = sendCommand(IDENTIFY_COMMAND, 0x00);
	//int device_revi = 0;
	if(received == 10){
		int checked = checkMessage();
		switch(checked){
		case INVALID_INPUT_SEQUENCE:
			std::cerr << "CAO returned an invalid message" << std::endl;
			tcflush(fd, TCIOFLUSH);
			return false;
		case CAO_ERROR:
			std::cerr << "The CAO board had problems, check documentation for error:" << std::endl;
			std::cerr << "Source: 0x"<< std::setfill('0') << std::setw(4) << std::hex << (input_buffer[4] + (input_buffer[5]<<8)) << std::endl;
			std::cerr << "Code:   0x"<< std::setfill('0') << std::setw(4) << std::hex << (input_buffer[7] + (input_buffer[8]<<8)) << std::endl;
			tcflush(fd, TCIOFLUSH);
			return false;
		}
	}
	if(input_buffer[7] + (input_buffer[8]<<8) != (unsigned int)REVNUMBER){
		std::cerr << "The revision numbers don't match. Be sure you checked the correct version from SVN." << std::endl;
		tcflush(fd, TCIOFLUSH);
		return false;
	}
	return true;
}

int SerialReader::sendCommand(unsigned char command, int addr, unsigned char opt1, unsigned char opt2){
	char message[10] = {0xFF, 0xFF, command, 0x00, addr, addr>>8, 0x00, opt1, opt2, 0x00};
	int written = write(fd, message, 10);
	if(written < 10){
		cerr << "Serial data writing failed." << endl;
	}
	fd_set fds;
	FD_ZERO(&fds);
	FD_SET(fd, &fds);
	struct timeval timeout = { 0, 250000 }; /* 250 ms seconds */
	int n = select(fd+1, &fds, NULL, NULL, &timeout);
	if(n < 0){
		std::cerr << "Serial data reading failed."<< std::endl;
		tcflush(fd, TCIOFLUSH);
		return -1;
	}else if(n == 0){
		std::cout << "Serial data reading timedout." << std::endl;
		tcflush(fd, TCIOFLUSH);
		return 0;
	}else{
		int tmp=read(fd, input_buffer, 10);
		tcflush(fd, TCIOFLUSH);
		return tmp;
	}
}

int SerialReader::deviceId(){
	return device_id;
}

int SerialReader::getTWIData(int id){
	int received = sendCommand(TWI_COMMAND, id);
	int check;
	if(received == 10){
		check = checkMessage();
		switch(check){
		case INVALID_INPUT_SEQUENCE:
			std::cerr << "CAO returned an invalid message" << std::endl;
			return -1;
		case CAO_ERROR:
			std::cerr << "The CAO board had problems, check documentation for error:" << std::endl;
			std::cerr << "Source: 0x"<< std::setfill('0') << std::setw(4) << std::hex << (input_buffer[4] + (input_buffer[5]<<8)) << std::endl;
			std::cerr << "Code:   0x"<< std::setfill('0') << std::setw(4) << std::hex << (input_buffer[7] + (input_buffer[8]<<8)) << std::endl;
			return -2;
		default:
			return (unsigned char)(input_buffer[7]) + (unsigned char)(input_buffer[8]<<8);
		}
	}else{
		std::cerr << "Less chars received: " << received << " in total." << std::endl;
		return -3;
	}
}

int SerialReader::checkMessage(){
	if(input_buffer[0] != 0xFF || input_buffer[1] != 0xFF || input_buffer[3] != 0x00 || input_buffer[6] != 0x00 || input_buffer[9] != 0x00){
		return INVALID_INPUT_SEQUENCE;
	}else if(input_buffer[2]==CAO_ERROR){
		return CAO_ERROR;
	}else{
		return 0;
	}
}
